summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/jni/native_input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/jni/native_input.cpp')
-rw-r--r--src/android/app/src/main/jni/native_input.cpp631
1 files changed, 631 insertions, 0 deletions
diff --git a/src/android/app/src/main/jni/native_input.cpp b/src/android/app/src/main/jni/native_input.cpp
new file mode 100644
index 000000000..ddf2f297b
--- /dev/null
+++ b/src/android/app/src/main/jni/native_input.cpp
@@ -0,0 +1,631 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common/fs/fs.h>
+#include <common/fs/path_util.h>
+#include <common/settings.h>
+#include <hid_core/hid_types.h>
+#include <jni.h>
+
+#include "android_config.h"
+#include "common/android/android_common.h"
+#include "common/android/id_cache.h"
+#include "hid_core/frontend/emulated_controller.h"
+#include "hid_core/hid_core.h"
+#include "input_common/drivers/android.h"
+#include "input_common/drivers/touch_screen.h"
+#include "input_common/drivers/virtual_amiibo.h"
+#include "input_common/drivers/virtual_gamepad.h"
+#include "native.h"
+
+std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
+
+bool IsHandheldOnly() {
+ const auto npad_style_set =
+ EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag();
+
+ if (npad_style_set.fullkey == 1) {
+ return false;
+ }
+
+ if (npad_style_set.handheld == 0) {
+ return false;
+ }
+
+ return !Settings::IsDockedMode();
+}
+
+std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
+ return filename.replace_extension();
+}
+
+bool IsProfileNameValid(std::string_view profile_name) {
+ return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
+}
+
+bool ProfileExistsInFilesystem(std::string_view profile_name) {
+ return Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input" /
+ fmt::format("{}.ini", profile_name));
+}
+
+bool ProfileExistsInMap(const std::string& profile_name) {
+ return map_profiles.find(profile_name) != map_profiles.end();
+}
+
+bool SaveProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ Settings::values.players.GetValue()[player_index].profile_name = profile_name;
+ map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index);
+ return true;
+}
+
+bool LoadProfile(std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ map_profiles.erase(profile_name);
+ return false;
+ }
+
+ LOG_INFO(Config, "Loading input profile `{}`", profile_name);
+
+ Settings::values.players.GetValue()[player_index].profile_name = profile_name;
+ map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index);
+ return true;
+}
+
+void ApplyControllerConfig(size_t player_index,
+ const std::function<void(Core::HID::EmulatedController*)>& apply) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ if (player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ handheld->EnableConfiguration();
+ player_one->EnableConfiguration();
+ apply(handheld);
+ apply(player_one);
+ handheld->DisableConfiguration();
+ player_one->DisableConfiguration();
+ handheld->SaveCurrentConfig();
+ player_one->SaveCurrentConfig();
+ } else {
+ auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
+ controller->EnableConfiguration();
+ apply(controller);
+ controller->DisableConfiguration();
+ controller->SaveCurrentConfig();
+ }
+}
+
+void ConnectController(size_t player_index, bool connected) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ if (player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ handheld->EnableConfiguration();
+ player_one->EnableConfiguration();
+ if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
+ if (connected) {
+ handheld->Connect();
+ } else {
+ handheld->Disconnect();
+ }
+ player_one->Disconnect();
+ } else {
+ if (connected) {
+ player_one->Connect();
+ } else {
+ player_one->Disconnect();
+ }
+ handheld->Disconnect();
+ }
+ handheld->DisableConfiguration();
+ player_one->DisableConfiguration();
+ handheld->SaveCurrentConfig();
+ player_one->SaveCurrentConfig();
+ } else {
+ auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
+ controller->EnableConfiguration();
+ if (connected) {
+ controller->Connect();
+ } else {
+ controller->Disconnect();
+ }
+ controller->DisableConfiguration();
+ controller->SaveCurrentConfig();
+ }
+}
+
+extern "C" {
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isHandheldOnly(JNIEnv* env,
+ jobject j_obj) {
+ return IsHandheldOnly();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadButtonEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_button_id, jint j_action) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState(
+ Common::Android::GetJString(env, j_guid), j_port, j_button_id, j_action != 0);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadAxisEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_stick_id, jfloat j_value) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition(
+ Common::Android::GetJString(env, j_guid), j_port, j_stick_id, j_value);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadMotionEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jlong j_delta_timestamp,
+ jfloat j_x_gyro, jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel,
+ jfloat j_z_accel) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState(
+ Common::Android::GetJString(env, j_guid), j_port, j_delta_timestamp, j_x_gyro, j_y_gyro,
+ j_z_gyro, j_x_accel, j_y_accel, j_z_accel);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onReadNfcTag(JNIEnv* env, jobject j_obj,
+ jbyteArray j_data) {
+ jboolean isCopy{false};
+ std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
+ static_cast<size_t>(env->GetArrayLength(j_data)));
+
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onRemoveNfcTag(JNIEnv* env, jobject j_obj) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo();
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* env, jobject j_obj,
+ jint j_id, jfloat j_x_axis,
+ jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(
+ j_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env, jobject j_obj,
+ jint j_id, jfloat j_x_axis,
+ jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(
+ j_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
+ jint j_id) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(j_id);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayButtonEventImpl(
+ JNIEnv* env, jobject j_obj, jint j_port, jint j_button_id, jint j_action) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState(
+ j_port, j_button_id, j_action == 1);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayJoystickEventImpl(
+ JNIEnv* env, jobject j_obj, jint j_port, jint j_stick_id, jfloat j_x_axis, jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition(
+ j_port, j_stick_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onDeviceMotionEvent(
+ JNIEnv* env, jobject j_obj, jint j_port, jlong j_delta_timestamp, jfloat j_x_gyro,
+ jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, jfloat j_z_accel) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState(
+ j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, j_z_gyro, j_x_accel, j_y_accel,
+ j_z_accel);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env,
+ jobject j_obj) {
+ EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_registerController(JNIEnv* env,
+ jobject j_obj,
+ jobject j_device) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->RegisterController(j_device);
+}
+
+jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputDevices(JNIEnv* env,
+ jobject j_obj) {
+ auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices();
+ jobjectArray jdevices = env->NewObjectArray(devices.size(), Common::Android::GetStringClass(),
+ Common::Android::ToJString(env, ""));
+ for (size_t i = 0; i < devices.size(); ++i) {
+ env->SetObjectArrayElement(jdevices, i,
+ Common::Android::ToJString(env, devices[i].Serialize()));
+ }
+ return jdevices;
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env,
+ jobject j_obj) {
+ map_profiles.clear();
+ const auto input_profile_loc =
+ Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input";
+
+ if (Common::FS::IsDir(input_profile_loc)) {
+ Common::FS::IterateDirEntries(
+ input_profile_loc,
+ [&](const std::filesystem::path& full_path) {
+ const auto filename = full_path.filename();
+ const auto name_without_ext =
+ Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));
+
+ if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) {
+ map_profiles.insert_or_assign(
+ name_without_ext, std::make_unique<AndroidConfig>(
+ name_without_ext, Config::ConfigType::InputProfile));
+ }
+
+ return true;
+ },
+ Common::FS::DirEntryFilter::File);
+ }
+}
+
+jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputProfileNames(
+ JNIEnv* env, jobject j_obj) {
+ std::vector<std::string> profile_names;
+ profile_names.reserve(map_profiles.size());
+
+ auto it = map_profiles.cbegin();
+ while (it != map_profiles.cend()) {
+ const auto& [profile_name, config] = *it;
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ it = map_profiles.erase(it);
+ continue;
+ }
+
+ profile_names.push_back(profile_name);
+ ++it;
+ }
+
+ std::stable_sort(profile_names.begin(), profile_names.end());
+
+ jobjectArray j_profile_names =
+ env->NewObjectArray(profile_names.size(), Common::Android::GetStringClass(),
+ Common::Android::ToJString(env, ""));
+ for (size_t i = 0; i < profile_names.size(); ++i) {
+ env->SetObjectArrayElement(j_profile_names, i,
+ Common::Android::ToJString(env, profile_names[i]));
+ }
+
+ return j_profile_names;
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isProfileNameValid(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name) {
+ return Common::Android::GetJString(env, j_name).find_first_of("<>:;\"/\\|,.!?*") ==
+ std::string::npos;
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_createProfile(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ if (ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles.insert_or_assign(
+ profile_name,
+ std::make_unique<AndroidConfig>(profile_name, Config::ConfigType::InputProfile));
+
+ return SaveProfile(profile_name, j_player_index);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_deleteProfile(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name) ||
+ Common::FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) {
+ map_profiles.erase(profile_name);
+ }
+
+ Settings::values.players.GetValue()[j_player_index].profile_name = "";
+ return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadProfile(JNIEnv* env, jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ return LoadProfile(profile_name, j_player_index);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_saveProfile(JNIEnv* env, jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ return SaveProfile(profile_name, j_player_index);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadPerGameConfiguration(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_selected_index,
+ jstring j_selected_profile_name) {
+ static constexpr size_t HANDHELD_INDEX = 8;
+
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ Settings::values.players.SetGlobal(false);
+
+ auto profile_name = Common::Android::GetJString(env, j_selected_profile_name);
+ auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(j_player_index);
+
+ if (j_selected_index == 0) {
+ Settings::values.players.GetValue()[j_player_index].profile_name = "";
+ if (j_player_index == 0) {
+ Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
+ }
+ Settings::values.players.SetGlobal(true);
+ emulated_controller->ReloadFromSettings();
+ return;
+ }
+ if (profile_name.empty()) {
+ return;
+ }
+ auto& player = Settings::values.players.GetValue()[j_player_index];
+ auto& global_player = Settings::values.players.GetValue(true)[j_player_index];
+ player.profile_name = profile_name;
+ global_player.profile_name = profile_name;
+ // Read from the profile into the custom player settings
+ LoadProfile(profile_name, j_player_index);
+ // Make sure the controller is connected
+ player.connected = true;
+
+ emulated_controller->ReloadFromSettings();
+
+ if (j_player_index > 0) {
+ return;
+ }
+ // Handle Handheld cases
+ auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
+ auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ if (player.controller_type == Settings::ControllerType::Handheld) {
+ handheld_player = player;
+ } else {
+ handheld_player = {};
+ }
+ handheld_controller->ReloadFromSettings();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_beginMapping(JNIEnv* env, jobject j_obj,
+ jint jtype) {
+ EmulationSession::GetInstance().GetInputSubsystem().BeginMapping(
+ static_cast<InputCommon::Polling::InputType>(jtype));
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getNextInput(JNIEnv* env,
+ jobject j_obj) {
+ return Common::Android::ToJString(
+ env, EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env, jobject j_obj) {
+ EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params,
+ jstring j_display_name) {
+ auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem();
+
+ // Clear all previous mappings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(button_id, {});
+ });
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(analog_id, {});
+ });
+ }
+
+ // Apply new mappings
+ auto device = Common::ParamPackage(Common::Android::GetJString(env, j_device_params));
+ auto button_mappings = input_subsystem.GetButtonMappingForDevice(device);
+ auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device);
+ auto display_name = Common::Android::GetJString(env, j_display_name);
+ for (const auto& button_mapping : button_mappings) {
+ const std::size_t index = button_mapping.first;
+ auto named_mapping = button_mapping.second;
+ named_mapping.Set("display", display_name);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(index, named_mapping);
+ });
+ }
+ for (const auto& analog_mapping : analog_mappings) {
+ const std::size_t index = analog_mapping.first;
+ auto named_mapping = analog_mapping.second;
+ named_mapping.Set("display", display_name);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(index, named_mapping);
+ });
+ }
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonParamImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_button) {
+ return Common::Android::ToJString(env, EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetButtonParam(j_button)
+ .Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setButtonParamImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_button_id, jstring j_param) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(j_button_id,
+ Common::ParamPackage(Common::Android::GetJString(env, j_param)));
+ });
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStickParamImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_stick) {
+ return Common::Android::ToJString(env, EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetStickParam(j_stick)
+ .Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStickParamImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_stick_id, jstring j_param) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(j_stick_id,
+ Common::ParamPackage(Common::Android::GetJString(env, j_param)));
+ });
+}
+
+jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv* env,
+ jobject j_obj,
+ jstring j_param) {
+ return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName(
+ Common::ParamPackage(Common::Android::GetJString(env, j_param))));
+}
+
+jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ const auto npad_style_set = hid_core.GetSupportedStyleTag();
+ std::vector<s32> supported_indexes;
+ if (npad_style_set.fullkey == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
+ }
+
+ if (npad_style_set.joycon_dual == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
+ }
+
+ if (npad_style_set.joycon_left == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
+ }
+
+ if (npad_style_set.joycon_right == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
+ }
+
+ if (j_player_index == 0 && npad_style_set.handheld == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
+ }
+
+ if (npad_style_set.gamecube == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
+ }
+
+ jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
+ env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
+ supported_indexes.data());
+ return j_supported_indexes;
+}
+
+jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStyleIndexImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index) {
+ return static_cast<s32>(EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetNpadStyleIndex(true));
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStyleIndexImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_style_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ auto type = static_cast<Core::HID::NpadStyleIndex>(j_style_index);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetNpadStyleIndex(type);
+ });
+ if (j_player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ ConnectController(j_player_index,
+ player_one->IsConnected(true) || handheld->IsConnected(true));
+ }
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isControllerImpl(JNIEnv* env,
+ jobject j_obj,
+ jstring jparams) {
+ return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().IsController(
+ Common::ParamPackage(Common::Android::GetJString(env, jparams))));
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getIsConnected(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(j_player_index));
+ if (j_player_index == 0 &&
+ controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
+ return hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true);
+ }
+ return controller->IsConnected(true);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_connectControllersImpl(
+ JNIEnv* env, jobject j_obj, jbooleanArray j_connected) {
+ jboolean isCopy = false;
+ auto j_connected_array_size = env->GetArrayLength(j_connected);
+ jboolean* j_connected_array = env->GetBooleanArrayElements(j_connected, &isCopy);
+ for (int i = 0; i < j_connected_array_size; ++i) {
+ ConnectController(i, j_connected_array[i]);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_resetControllerMappings(
+ JNIEnv* env, jobject j_obj, jint j_player_index) {
+ // Clear all previous mappings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(button_id, {});
+ });
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(analog_id, {});
+ });
+ }
+}
+
+} // extern "C"